////////////////////////////////////////////////////////////////////////////
//
//  CryEngine Source File.
//  Copyright (C), Crytek, 1999-2009.
// -------------------------------------------------------------------------
//  File name:   TimelinePreprocessor.cpp
//  Version:     v1.00
//  Created:     25/12/2009 by Sergey Mikhtonyuk
//  Description: 
// -------------------------------------------------------------------------
//  History:
//
////////////////////////////////////////////////////////////////////////////
#include "StdAfx.h"
#include "TimelinePreprocessor.h"

//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////

namespace Telemetry
{

struct STimelineCrawler
{
	void setTimeline(CTelemetryTimeline* tl)
	{
		timeline = tl;
	}

	bool canAdvance() const
	{
		return (position + 1) < timeline->GetEventCount();
	}

	int64 getTime()
	{
		if(position < 0 || position >= timeline->GetEventCount())
			return -1;
		return timeline->GetEvent(position).timeMs;
	}

	int64 getNextTime()
	{
		int pos = position + 1;
		if(pos < 0 || pos >= timeline->GetEventCount())
			return -1;
		return timeline->GetEvent(pos).timeMs;
	}

	bool advance(int64 time)
	{
		if(getNextTime() > time)
			return false;

		++position;
		return true;
	}

	bool advance()
	{
		return advance(getNextTime());
	}

	STelemetryEvent& getEvent()
	{
		CRY_ASSERT(position >= 0 && position < timeline->GetEventCount());
		return timeline->GetEvent((size_t)position);
	}

	STelemetryEvent& getNextEvent()
	{
		CRY_ASSERT(position < timeline->GetEventCount() - 1);
		return timeline->GetEvent((size_t)position + 1);
	}

	STimelineCrawler() 
		: timeline(0)
		, position(-1)
		, processing(true)
		, advanced(false)
	{ }

public:
	CTelemetryTimeline* timeline;
	int position;
	bool advanced;
	bool processing;
};

//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////

void CTimelinePreprocessor::DeduceEventPositions(CTelemetryTimeline** timelines, size_t numTimelines, size_t posTimeline)
{
	// Setup crawlers
	STimelineCrawler* crawlers = new STimelineCrawler[numTimelines];
	STimelineCrawler& positionCrawler = crawlers[posTimeline];

	for(int i = 0; i < numTimelines; ++i)
		crawlers[i].setTimeline(timelines[i]);


	int advanceable;
	int64 smallest_step;

	while(true)
	{
		advanceable = 0;
		smallest_step = std::numeric_limits<int64>::max();

		// Calculate minimal step
		for(size_t i = 0; i < numTimelines; ++i)
		{
			if(crawlers[i].processing && crawlers[i].canAdvance())
			{
				++advanceable;
				int64 t = crawlers[i].getNextTime();
				if(t < smallest_step)
					smallest_step = t;
			}
			else
				crawlers[i].processing = false;
		}


		if(!advanceable)
			break;


		// Advance crawlers
		for(int i = 0; i < numTimelines; ++i)
		{
			if(crawlers[i].processing)
				crawlers[i].advanced = crawlers[i].advance(smallest_step);
		}

		if(positionCrawler.position == -1)
			positionCrawler.advance();

		for(size_t i = 0; i < numTimelines; ++i)
		{
			if(crawlers[i].processing && crawlers[i].advanced && i != posTimeline)
			{
				if(positionCrawler.canAdvance())
				{
					STelemetryEvent& pos1 = positionCrawler.getEvent();
					STelemetryEvent& pos2 = positionCrawler.getNextEvent();
					STelemetryEvent& e = crawlers[i].getEvent();

					float a = (float)(e.timeMs - pos1.timeMs) / (float)(pos2.timeMs - pos1.timeMs);
					e.position = LERP(pos1.position, pos2.position, a);
				}
				else
				{
					crawlers[i].getEvent().position = positionCrawler.getEvent().position;
				}
			}
		}
	}

	delete[] crawlers;

	for(size_t i = 0; i < numTimelines; ++i)
		timelines[i]->UpdateBBox();
}

//////////////////////////////////////////////////////////////////////////

}